6704: Azure Blob Storage URL Signing and Refresh Support#11137
6704: Azure Blob Storage URL Signing and Refresh Support#11137adamfisher wants to merge 5 commits intodanny-avila:mainfrom
Conversation
|
BUMP |
|
@adamfisher Thats very cool and exactly what we are looking for, regarding some Security Risks Wokring with "Public" Azure Blob Storage. Any Timeline Idea on this ? |
|
I'm not sure when they will get to reviewing this PR. I've brought it up many times on discord in two different channels asking for an ETA. It might help if others are asking about it too on discord. |
There was a problem hiding this comment.
Pull request overview
This PR implements signed URL (SAS token) support for Azure Blob Storage to enable private container access, achieving feature parity with the existing S3 implementation. When AZURE_STORAGE_PUBLIC_ACCESS=false, files are served via time-limited signed URLs that automatically refresh before expiration. The implementation supports two signing methods that are automatically selected based on configuration: Account Key signing (when connection string is set) and User Delegation SAS (when using Managed Identity).
Changes:
- Added Azure Blob Storage URL signing with automatic refresh functionality
- Implemented dynamic cache duration based on URL expiry time to prevent premature expiration
- Fixed avatar URL generation to remove erroneous
?manual=truesuffix that broke signed URLs - Integrated Azure URL refresh logic into file routes, user controller, and agent controllers parallel to existing S3 implementation
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 19 comments.
Show a summary per file
| File | Description |
|---|---|
api/server/services/Files/Azure/crud.js |
Core implementation: added URL signing, refresh logic, and helper functions for SAS token generation |
api/server/services/Files/Azure/images.js |
Fixed avatar processing to return clean URLs without manual flag suffix |
api/server/routes/files/files.js |
Added Azure file URL refresh support with dynamic cache time calculation |
api/server/controllers/UserController.js |
Integrated Azure avatar URL refresh for user profile pictures |
api/server/controllers/agents/v1.js |
Added Azure avatar refresh for agent avatars in list and detail views |
packages/data-provider/src/config.ts |
Added AZURE_EXPIRY_INTERVAL cache key enum |
api/cache/getLogStores.js |
Configured Azure expiry interval cache store with 30-minute TTL |
api/test/server/services/Files/Azure/crud.test.js |
Added unit tests for URL refresh and blob path extraction logic |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ainer # Conflicts: # api/server/controllers/agents/v1.js # api/server/services/Files/Azure/crud.js
|
danny-avila's CHANGES_REQUESTED (the critical ones):
Copilot actionable comments:
|
|
@danny-avila Are we going to merge this? |
|
Would love to see this implemented. |
Summary
This PR adds signed URL (SAS token) support for Azure Blob Storage, bringing feature parity with the existing S3 implementation. When
AZURE_STORAGE_PUBLIC_ACCESS=false, files are served via time-limited signed URLs that are automatically refreshed before expiration.See: [Enhancement]: Azure Blob Storage private container support
Documentation PR: LibreChat-AI/librechat.ai#474
Features
AZURE_STORAGE_PUBLIC_ACCESS=falseAZURE_STORAGE_CONNECTION_STRINGis setAZURE_STORAGE_ACCOUNT_NAMEis set (Managed Identity)Files Changed
Core Implementation
api/server/services/Files/Azure/crud.jsgetSignedAzureURL()- generates SAS tokens via Account Key or User DelegationgetUserDelegationKey()- obtains and caches delegation keys (7-day lifespan, refreshed 5 min before expiry)needsRefreshAzure()- checks if URL needs refresh based on expiry or access mode changeextractBlobPathFromAzureUrl()- extracts blob path from Azure URLgetNewAzureURL()- generates fresh signed or plain URL based on current access moderefreshAzureFileUrls()- batch refresh for file arraysrefreshAzureUrl()- single file URL refreshsaveBufferToAzure()- returns signed URL when private access enabledstreamFileToAzure()- returns signed URL when private access enabledapi/server/services/Files/Azure/images.jsprocessAzureAvatar()- removed erroneous?manual=truesuffix that broke signed URLsRoute Integration
api/server/routes/files/files.jsgetRefreshCacheTime()helper for dynamic cache duration based on URL expiryAZURE_EXPIRY_INTERVAL) from S3Controller Integration
api/server/controllers/UserController.jsgetUserController()parallel to existing S3 logicapi/server/controllers/agents/v1.jsrefreshListAvatars()andgetAgentHandler()Cache Configuration
packages/data-provider/src/config.tsCacheKeys.AZURE_EXPIRY_INTERVALenum valueapi/cache/getLogStores.jsAZURE_EXPIRY_INTERVALcache store with 30-minute TTLTests
api/test/server/services/Files/Azure/crud.test.jsneedsRefreshAzure()with various URL statesextractBlobPathFromAzureUrl()Documentation
pages/docs/configuration/cdn/azure.mdxAZURE_URL_EXPIRY_SECONDSenvironment variableEnvironment Variables
AZURE_URL_EXPIRY_SECONDSAZURE_STORAGE_PUBLIC_ACCESSfalseenables URL signing,trueuses plain URLsAZURE_STORAGE_CONNECTION_STRINGAZURE_STORAGE_ACCOUNT_NAMEBreaking Changes
None. Existing deployments with
AZURE_STORAGE_PUBLIC_ACCESS=truecontinue to work unchanged.Testing Steps
Prerequisites
filesTest 1: Account Key Signing (Connection String)
Setup
Steps
sv=,se=,sig=)AZURE_URL_EXPIRY_SECONDS=60for faster testing)Expected Results
Test 2: User Delegation SAS (Managed Identity)
Setup
Deploy to Azure Container Apps, App Service, or VM with:
# .env (NO connection string) AZURE_STORAGE_ACCOUNT_NAME=yourAccount AZURE_STORAGE_PUBLIC_ACCESS=false AZURE_CONTAINER_NAME=files AZURE_URL_EXPIRY_SECONDS=300Azure Role Assignments
Assign to the Managed Identity:
Storage Blob Data ContributorStorage Blob DelegatorSteps
skoid=,sktid=,skt=,ske=)Expected Results
Test 3: User Avatar Upload and Refresh
Steps
AZURE_STORAGE_PUBLIC_ACCESS=false)?manual=truesuffixExpected Results
?manual=true)Test 4: Agent Avatar Refresh
Steps
Expected Results
Test 5: Public Access Mode
Setup
Steps
Expected Results
Test 6: Access Mode Transition (Public → Private)
Steps
AZURE_STORAGE_PUBLIC_ACCESS=true, upload an imageAZURE_STORAGE_PUBLIC_ACCESS=falseand restartExpected Results
Test 7: Dynamic Cache Duration
Setup
AZURE_URL_EXPIRY_SECONDS=120 # 2 minutesSteps
Setup 2
AZURE_URL_EXPIRY_SECONDS=7200 # 2 hoursSteps
Expected Results
Test 8: Local Development with Azurite
Setup
AZURE_STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;" AZURE_STORAGE_PUBLIC_ACCESS=false AZURE_URL_EXPIRY_SECONDS=300Steps
Expected Results
Test 9: Unit Tests
Expected Results
needsRefreshAzurecorrectly identifies expired URLsneedsRefreshAzurecorrectly identifies valid URLsextractBlobPathFromAzureUrlextracts paths correctlyRollback Plan
If issues occur, revert to public access:
Or revert to connection string if Managed Identity issues: